import { Context, Schema, Dict, Time, h, Session } from 'koishi'
import { Kook, KookBot } from "@koishijs/plugin-adapter-kook";
import {} from 'koishi-plugin-adapter-onebot';
import { readFileSync, existsSync } from 'fs'
import { resolve } from 'path'
import { config } from 'process';

export const name = 'group-manage'

export const inject = {
    required: ["database"]
};

const pkg = JSON.parse(
  readFileSync(resolve(__dirname, '../package.json'), 'utf-8')
)

interface BlockingRule {
  enable: boolean
  blockingWords: string[]
  mute: boolean
  muteDuration: number
  recall: boolean
  tipInGroup: boolean
  tipInPrivate: boolean
  tipMessage: string
  reactEmojiInGroup: boolean
  reactEmojiCodeString:  string
}


export interface Config {
  enableSessionExecuteReaction: boolean

  enableKoishiDbAuthCheck: boolean
  minAuthLevel: number
  enableOnebotAdminCheck: boolean
  enableWhiteListCheck: boolean
  userAuthWhiteList: string[]

  blockingRules: Dict<BlockingRule, string>
  ReportQqIdList: string[]
  ReportKookChannelIdList: string[]

  banDuration: number

  verboseConsoleOutput: boolean
}


export const Config: Schema<Config> = Schema.intersect([
  Schema.object({
    fork的版本号: Schema.string().default(pkg.version).disabled(),
  }).description('vincentzyu fork此插件时候的原始版本：1.3.1'),

  Schema.object({
    enableKoishiDbAuthCheck: Schema.boolean().default(false).description("是否启用koishi数据库权限校验"),
    minAuthLevel: Schema.number().min(0).max(4).step(1).default(3).description("最低权限等级, 建议3别动"),
    enableOnebotAdminCheck: Schema.boolean().default(true).description("是否启用onebot群主管理员校验"),
    enableWhiteListCheck: Schema.boolean().default(true).description("是否启用白名单校验"),
    userAuthWhiteList: Schema.array(String).role('tablea').default(["1830540513"]).description("白名单用户列表"),
  }).description("用户权限校验设置"),

  Schema.object({
    enableSessionExecuteReaction: Schema.boolean().default(true)
  }).description('发送执行反馈，比如： 已执行禁言 等等'),

  Schema.object({
    blockingRules: Schema.dict(Schema.object({
      enable: Schema.boolean().description('是否启用').default(true),
      blockingWords: Schema.array(String).description('违禁词列表 (可使用正则表达式)').default([]),
      mute: Schema.boolean().description('检测到违禁词后是否禁言').default(false),
      muteDuration: Schema.natural().role('ms').description('禁言时长 (单位为毫秒)').default(10 * Time.minute),
      recall: Schema.boolean().description('检测到违禁词后是否撤回').default(false),
      tipInGroup: Schema.boolean().description('是否在检测到违禁词后进行提示').default(true),
      tipInPrivate: Schema.boolean().description('是否在检测到违禁词后进行私聊提示').default(true),
      tipMessage: Schema.string().description('提示消息').default('检测到违禁词'),
      reactEmojiInGroup: Schema.boolean().description('是否在群组消息中添加表情反应').default(false),
      reactEmojiCodeString: Schema.string().description('表情反应字符串').default('10068'),
    }).description('群组平台与群组 ID, 格式:`platform:guildId`, 例如:`red:123456`')).description('规则列表'),

    ReportQqIdList: Schema.array(String).description("私聊报告的QQ账号"),

    ReportKookChannelIdList: Schema.array(String).description('管理员Kook频道ID').default([]),

  }).description('违禁词检测设置'),

  Schema.object({
    banDuration: Schema.natural().role('ms').description('ban 和 ban-me 指令默认禁言时长 (单位为毫秒)').default(15 * Time.hour),
  }).description('指令默认值设置'),

  Schema.object({
    verboseConsoleOutput: Schema.boolean().description('是否在控制台输出详细信息').default(false),
  }).description('调试选项'),
])

export const usage: string = `
使用本插件的指令进行群管操作时，支持三种校验方式：
1. 基于 Koishi 数据库的Authority权限校验(开启auth插件，去用户资料页绑定platform和userid)
2. 基于 OneBot 协议的权限校验(仅适用于onebot平台，群主和管理员自动获取权限)
3. 基于 配置文件用户id白名单的权限校验

三种都是可选的，可以从中选择[0,3]种，
三种是或关系，其中至少一个校验通过即可

koishi auth权限设置教程: https://koishi.chat/zh-CN/manual/usage/customize.html#%E7%94%A8%E6%88%B7%E6%9D%83%E9%99%90
`

export function apply(ctx: Context, cfg: Config) {
  ctx.i18n.define('zh-CN', require('./locales/zh-CN'))

  async function hasPermission(session: Session): Promise<boolean> {

    if ( cfg.enableKoishiDbAuthCheck ){
      const bindingEntries = await ctx.database.get('binding', { pid: session.userId });
      if ( bindingEntries && bindingEntries.length>0 ){
        const b0 = bindingEntries[0];
        const userEntries = await ctx.database.get('user', { id: b0.aid });
        if ( userEntries && userEntries.length>0 ){
          const u0 = userEntries[0];
          if ( u0.authority >= 3 ){
            return true;
          }
        }
      }
    }

    if ( cfg.enableOnebotAdminCheck ){
      const res = await session.bot.getGuildMember(session.guildId, session.userId);
      if (res.roles.includes("owner") || res.roles.includes("admin")) {
        return true;
      }
    }

    if ( cfg.enableWhiteListCheck ){
      if ( cfg.userAuthWhiteList.includes(session.userId) ){
        return true;
      }
    }


  }

  ctx.middleware(async (session, next) => {
    if ( session.userId === session.bot.selfId )
      return;

    if (session.gid in cfg.blockingRules) {
      if (cfg.verboseConsoleOutput) {
        ctx.logger.info(`session.gid = ${session.gid}`)
        ctx.logger.info(`session.event.message.elements = ${JSON.stringify(session.event.message.elements)}`)
      }
      const rule = cfg.blockingRules[session.gid]
      if (!rule.enable) return next()

      // let hit = false

      // for (const word of rule.blockingWords) {
      //   const re = new RegExp(word)
      //   const include = session.event.message.elements.some(value => {
      //     if (value.type === 'text') {
      //       return re.test(value.attrs.content)
      //     }
      //     return false
      //   })
      //   if (include) {
      //     hit = true
      //     break
      //   }
      // }


      let hit = false
      let hit_words: string[] = []

      for (const element of session.event.message.elements) {
        if (element.type === 'text') {
          const content = element.attrs.content;
          for (const word of rule.blockingWords) {
            // 跳过空字符串，避免无限循环
            if (!word || word.trim() === '') continue;
            
            // const re = new RegExp(word, 'g');
            const re = new RegExp(`\\b${word}\\b`, 'g');
            let match;
            while ((match = re.exec(content)) !== null) {
              hit = true;
              hit_words.push(match[0]);
              // await session.send(`对应的word: ${word.slice(0,20)}`)
              if (cfg.verboseConsoleOutput)
                ctx.logger.info(`match[0] = ${match[0]}`);
              // ctx.logger.info(`match[1] = ${match[1]}`);
              
              // 防止无限循环：如果匹配到空字符串，手动推进lastIndex
              if (match[0] === '') {
                re.lastIndex++;
              }
            }
          }
        }
      }

      if (hit) {
        // rule.tipInGroup && await session.send(session.text('group-manage.blocking-word.hit'))
        // rule.tipInGroup && await session.send(session.text('group-manage.blocking-word.hit') + `, 违规类型: ${rule.tipMessage}, 违规词语：${hit_word}` )
        const formattedHitWords = hit_words.map((word, index) => `${index + 1}.${word}`).join(', ');
        const hitTipMsg = session.text('group-manage.blocking-word.hit') + `, 违规类型: ${rule.tipMessage}, 违规词语：${formattedHitWords}`;
        rule.tipInGroup && await session.send(hitTipMsg);

        if ( rule.reactEmojiInGroup && session.onebot ){

          await session.onebot._request(
            "set_group_reaction",
            {
              "group_id": session.guildId,
              "message_id": session.event.message.id,
              "code": rule.reactEmojiCodeString,
              "is_add": true
            }
          )
          await session.onebot._request(
            'set_msg_emoji_like', 
            { 
              message_id: session.event.message.id, 
              emoji_id: rule.reactEmojiCodeString
            }
          )

        }

        if (rule.tipInPrivate) {
          const guildInfo = await session.bot.getGuild(session.guildId);
          const sessionUser = await session.bot.getUser(session.userId, session.event.guild.id);
          const allTextElements = session.event.message.elements
            .filter(value => value.type === 'text')  // 过滤出文字消息段
            .map(value => value.attrs.content)       // 提取每个消息段的文本内容
            .join('\n');
          let hitTipPrivateMessage = hitTipMsg + '\n------------------'
            + `\n群号:${session.guildId}, 群名:${guildInfo.name}\n`
            + `\nqq号:${session.userId}, qq名:${session.username}\n`
            // + h.image(sessionUser.avatar)
            + `\n时间：${formatDate(new Date())}\n`
            + `\n所有文字消息段：${allTextElements}`;

          hitTipPrivateMessage = "\n=============\n\n" + hitTipPrivateMessage + "\n\n=============\n";
          for (const qq_acc_id of cfg.ReportQqIdList) {
            // await session.bot.sendPrivateMessage(qq_acc_id, hitTipPrivateMessage );
            try {
              await session.bot.sendPrivateMessage(qq_acc_id, hitTipPrivateMessage);
            } catch (error) {
              ctx.logger.error(`向管理员 ${qq_acc_id} 发送私聊消息失败: ${error}`);
            }
          }

          const kookBot = ctx.bots.find(bot => bot.platform === 'kook');

          for (const kook_channel_id of cfg.ReportKookChannelIdList) {
            try {
              await kookBot.sendMessage(kook_channel_id, hitTipPrivateMessage);
            } catch (error) {
              ctx.logger.error(`向Kook频道 ${kook_channel_id} 发送频道失败: ${error}`);
            }
          }

        }

        const { event } = session
        if (rule.recall) {
          await session.bot.deleteMessage(event.channel.id, event.message.id)
          rule.tipInGroup && await session.send(session.text('group-manage.blocking-word.recall'))
        }
        if (rule.mute) {
          await session.bot.muteGuildMember(event.guild.id, event.user.id, rule.muteDuration)
          rule.tipInGroup && await session.send(session.text('group-manage.blocking-word.mute'))
        }
        return
      }
    }
    return next()
  })

  const command = ctx.command('group-manage')
    .action(async ({ session }) => {
      let msg = `指令：group-manage\n\t群组管理指令\n`;
      if ( await hasPermission(session) ){
        msg += [
          `可用的子指令有:`,
          `\tban 艾特用户 时长 单位 \t（禁言用户）`,
          `\tunban 艾特用户 \t\t（取消禁言用户）`,
          `\tmute-all \t\t\t\t（禁言所有用户）`,
          `\tunmute-all \t\t\t（取消禁言所有用户）`,
          `\tkick 艾特用户 \t\t\t（踢出用户）`,
          `\tdelmsg （引用/回复消息使用）（删除引用的消息）`
        ].join('\n')
      }
      await session.send(msg);
    })

  command.subcommand('ban <user:user> <duration:posint> <unit>', )
    .alias('mute', '禁言')
    .action(async ({ session }, user, duration, unit) => {
      if ( !await hasPermission(session) )
        return session.text('group-manage.no-permission');
      if (!user) {
        // return session.text('.missing-user');
        if ( cfg.enableSessionExecuteReaction )
          await session.send(session.text('.missing-user'));
        return;
      }
      if (!duration) {
        duration = cfg.banDuration
      } else {
        duration = parseDuration(duration, unit)
        if (duration === undefined) return session.text('.missing-duration')
      }
      const userId = user.replace(session.platform + ':', '')
      await session.bot.muteGuildMember(session.guildId, userId, duration)
      // return session.text('.executed')
      if ( cfg.enableSessionExecuteReaction )
        await session.send( session.text('.executed') )
      return;
    })

  // command.subcommand('ban-me <duration:posint> <unit>')
  //   .alias('self-ban', 'mute-me', '自我禁言')
  //   .action(async ({ session }, duration, unit) => {
  //     if (!duration) {
  //       duration = cfg.banDuration
  //     } else {
  //       duration = parseDuration(duration, unit)
  //       if (duration === undefined) return session.text('.missing-duration')
  //     }
  //     await session.bot.muteGuildMember(session.guildId, session.userId, duration)
  //     return session.text('.executed')
  //   })

  command.subcommand('unban <user:user>', )
    .alias('unmute', '取消禁言')
    .action(async ({ session }, user) => {
      if ( !await hasPermission(session) )
        return session.text('group-manage.no-permission');
      if (!user) return session.text('.missing-user')
      const userId = user.replace(session.platform + ':', '')
      await session.bot.muteGuildMember(session.guildId, userId, 0)
      // return session.text('.executed')
      if ( cfg.enableSessionExecuteReaction )
        await session.send( session.text('.executed') )
      return;
    })

  command.subcommand('delmsg', )
    .alias('撤回消息')
    .action(async ({ session }) => {
      if ( !await hasPermission(session) )
        return session.text('group-manage.no-permission');
      if (!session.quote) return session.text('.missing-quote')
      await session.bot.deleteMessage(session.channelId, session.quote.id)
      // return session.text('.executed')
      if ( cfg.enableSessionExecuteReaction )
        await session.send( session.text('.executed') )
      return;
    })

  command.subcommand('kick <user:user>', )
    .alias('踢', '踢出群聊')
    .action(async ({ session }, user) => {
      if ( !await hasPermission(session) )
        return session.text('group-manage.no-permission');
      if (!user) return session.text('.missing-user')
      const userId = user.replace(session.platform + ':', '')
      await session.bot.kickGuildMember(session.guildId, userId)
      // return session.text('.executed')
      if ( cfg.enableSessionExecuteReaction )
        await session.send( session.text('.executed') )
      return;
    })

  command.subcommand('mute-all', )
    .alias('全员禁言')
    .alias('哈姆杀所有')
    .action(async ({ session }) => {
      if ( !await hasPermission(session) )
        return session.text('group-manage.no-permission');
      const { platform, guildId } = session
      switch (platform) {
        case 'red':
          await session.bot.internal.muteGroup({
            group: guildId,
            enable: true
          })
          break
        case 'onebot':
          await session.bot.internal.setGroupWholeBan(guildId, true)
          break
        case 'kritor':
          await session.bot.internal.setGroupWholeBan(guildId, true)
          break
        default:
          // return session.text('.unsupported-platform')
          if ( cfg.enableSessionExecuteReaction )
            await session.send( session.text('.unsupported-platform') )
          return;
      }
      // return session.text('.executed')
      if ( cfg.enableSessionExecuteReaction )
        await session.send( session.text('.executed') )
      return;
    })

  command.subcommand('unmute-all', )
    .alias('取消全员禁言')
    .alias('舍利子回魂')
    .action(async ({ session }) => {
      if ( !await hasPermission(session) )
        return session.text('group-manage.no-permission');
      const { platform, guildId } = session
      switch (platform) {
        case 'red':
          await session.bot.internal.muteGroup({
            group: guildId,
            enable: false
          })
          break
        case 'onebot':
          await session.bot.internal.setGroupWholeBan(guildId, false)
          break
        case 'kritor':
          await session.bot.internal.setGroupWholeBan(guildId, false)
          break
        default:
          // return session.text('.unsupported-platform')
          if ( cfg.enableSessionExecuteReaction )
            await session.send( session.text('.unsupported-platform') )
          return;
      }
      // return session.text('.executed')
      if ( cfg.enableSessionExecuteReaction )
        await session.send( session.text('.executed') )
      return;
    })
}

function parseDuration(duration: number, unit: string): number | undefined {
  switch (unit) {
    case '秒':
    case '秒钟':
    case 's':
      return duration * 1000
    case '分':
    case '分钟':
    case 'min':
      return duration * 60 * 1000
    case '时':
    case '小时':
    case 'h':
      return duration * 60 * 60 * 1000
    case '天':
    case 'd':
      return duration * 24 * 60 * 60 * 1000
    default:
      return undefined
  }
}

function formatDate(date) {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份是从0开始的，所以要加1
  const day = String(date.getDate()).padStart(2, '0');
  const hours = String(date.getHours()).padStart(2, '0');
  const minutes = String(date.getMinutes()).padStart(2, '0');
  const seconds = String(date.getSeconds()).padStart(2, '0');

  return `${year}年${month}月${day}日 ${hours}:${minutes}:${seconds}`;
}